package de.koller.oceanFramework.gl.util;

import de.koller.oceanFramework.OceanException;
import java.nio.ByteBuffer;
import org.lwjgl.opengl.GL11;

public class ParticleTexture extends RasterManager implements IRenderableImage {

	public static int MIN_FILTER = GL11.GL_LINEAR;
	public static int MAG_FILTER = GL11.GL_LINEAR;
	
	private final float u, v;
	private final int textureID;
	private final ByteBuffer buffer;
	
	private ParticleTexture( int textureID, int w, int h ) {
		super( w, h );
		
		u = 1;
		v = 1;
		this.textureID = textureID;
		
		pixels = new byte[ w*h*4 ];
		for( int i = 0; i < pixels.length; i++ )
			pixels[i] = (byte)0x0;
		
		GL11.glBindTexture( GL11.GL_TEXTURE_2D, textureID );	
		GL11.glTexParameteri( GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, MAG_FILTER );
		GL11.glTexParameteri( GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, MIN_FILTER );
		
		buffer = ByteBuffer.allocateDirect( pixels.length );
		buffer.put( pixels ).flip();
		
		GL11.glTexImage2D( GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, w, h, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer );
		this.flush();
	}
	
	public static ParticleTexture generate( int w, int h ) {
		GL11.glEnable( GL11.GL_TEXTURE_2D );
		int id = GL11.glGenTextures();
		return new ParticleTexture( id, w, h );
	}
	
	public void bind() {
		GL11.glEnable( GL11.GL_TEXTURE_2D );
		GL11.glBindTexture( GL11.GL_TEXTURE_2D, textureID );
	}
	
	public void flush() {
		this.bind();
		buffer.put( pixels ).flip();
		GL11.glTexSubImage2D( GL11.GL_TEXTURE_2D, 0, 0, 0, width, height, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer );
		changed = false;
	}
	
	/** use this.bind before! */
	public void flush( int x, int y, int w, int h, byte[] data ) {
		if( x < 0 || x + w > width || y < 0 || y + h > height )
			throw new IndexOutOfBoundsException( "Texture: " + width + "x" + height + 
					" RequestBound: " + x + " / " + y + " / " + w + " / " + h );
		
		ByteBuffer b = ByteBuffer.allocateDirect( data.length );
		b.put( data ).flip();
		GL11.glTexSubImage2D( GL11.GL_TEXTURE_2D, 0, x, y, w, h, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, b );
		changed = false;
	}
	
	/**Draws this texture in the specific rectangle. After this call, only TEXTURE_2D
	 * is enabled and this texture ist bound. Any other operation have to be set before.
	 * Also it expect an coordinatesystem which y-achse ist upside down.
	 * Think of flushing if you have changed the content! Note that this method does not
	 * use DisplayLists. If you want to render with these you have to use the id instead.
	 * @todo enabling vertexcolor.
	 * @param x the x-position of the rectangle
	 * @param y the y-position of the rectangle
	 * @param w the width of the rectangle
	 * @param h the height of the reactangle
	 * @param z the z-position of the rectangle, this ist only nessesary if elements overlap. */
	@Override
	public void draw2D( float x, float y, float w, float h, float z ) {
		draw2D( x, y, w, h, z, 0, 0, u, v );	
	}

	@Override
	public void draw2D( float x, float y, float w, float h, float z, float u0, float v0, float u1, float v1 ) {
		if( changed )
			System.err.println( "Warning: draws Texture without flushing!" );
		
//		GL13.glActiveTexture( GL13.GL_TEXTURE0 );
//		GL11.glTexEnvi( GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE );
		this.bind();
		
		GL11.glBegin( GL11.GL_QUADS );
			GL11.glTexCoord2f( u0, v0 );
			GL11.glVertex3f( x, y, z );
			
			GL11.glTexCoord2f( u0, v1 );
			GL11.glVertex3f( x, y+h, z );
			
			GL11.glTexCoord2f( u1, v1 );
			GL11.glVertex3f( x+w, y+h, z );
			
			GL11.glTexCoord2f( u1, v0 );
			GL11.glVertex3f( x+w, y, z );
		GL11.glEnd();	
	}

	@Override
	public void draw2D( float x, float y, float w, float h, float z, float u0, float v0, float u1, float v1, float u2, float v2, float u3, float v3 ) {
		if( changed )
			System.err.println( "Warning: draws Texture without flushing!" );
		
//		GL13.glActiveTexture( GL13.GL_TEXTURE0 );
//		GL11.glTexEnvi( GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE );
		this.bind();
		
		draw2DFast( x, y, w, h, z, u0, v0, u1, v1, u2, v2, u3, v3 );
	}

	public void draw2DFast( float x, float y, float w, float h, float z, float u0, float v0, float u1, float v1, float u2, float v2, float u3, float v3 ) {	
		GL11.glBegin( GL11.GL_QUADS );
			GL11.glTexCoord2f( u0*u, v0*v );
			GL11.glVertex3f( x, y, z );
			
			GL11.glTexCoord2f( u1*u, v1*v );
			GL11.glVertex3f( x, y+h, z );
			
			GL11.glTexCoord2f( u2*u, v2*v );
			GL11.glVertex3f( x+w, y+h, z );
			
			GL11.glTexCoord2f( u3*u, v3*v );
			GL11.glVertex3f( x+w, y, z );
		GL11.glEnd();	
	}
	
	public int getTextureID() {
		return textureID;
	}

	public float getU() {
		return u;
	}

	public float getV() {
		return v;
	}

	@Deprecated
	public int getSize() {
		throw new UnsupportedOperationException();
	}
	
}
